package com.sky.utils;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.Headers;
import com.qcloud.cos.auth.BasicSessionCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.auth.COSSigner;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.http.HttpMethodName;
import com.qcloud.cos.model.PutObjectRequest;
import com.qcloud.cos.model.PutObjectResult;
import com.qcloud.cos.region.Region;
import com.sky.exception.CommonException;
import com.tencent.cloud.CosStsClient;
import com.tencent.cloud.Policy;
import com.tencent.cloud.Response;
import com.tencent.cloud.Statement;
import com.tencent.cloud.cos.util.Jackson;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;

import javax.annotation.PreDestroy;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
@Data
@Slf4j
public class QcloudUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private String secretId;
    private String secretKey;
    private String bucketName;
    private String regionName;
    private String bucketUrl;

    // Redis缓存key前缀
    private static final String REDIS_COS_SECRET_PREFIX = "cos:tmp_secret:";
    // 缓存时间：25分钟（临时密钥30分钟有效期，提前5分钟过期避免边界问题）
    private static final long CACHE_EXPIRE_MINUTES = 25;

    /**
     * 构造函数 - 与COSClientConfig兼容
     */
    public QcloudUtil(String secretId, String secretKey, String bucketName, String regionName, String bucketUrl) {
        this.secretId = secretId;
        this.secretKey = secretKey;
        this.bucketName = bucketName;
        this.regionName = regionName;
        this.bucketUrl = bucketUrl;
    }

    /**
     * 解析输入，获取 COS 对象的 key
     * @param input
     * @return key对象
     */
    public String getKey(String input) {
        if (input == null || input.isEmpty()) {
            throw new IllegalArgumentException("COS key or URL cannot be empty");
        }

        // 如果是 URL
        if (input.contains(".myqcloud.com/")) {
            int index = input.indexOf(".myqcloud.com/");
            String key = input.substring(index + ".myqcloud.com/".length());
            // 使用 URI，不会把 '+' 解码成空格
            return java.net.URLDecoder.decode(key.replace("+", "%2B"), java.nio.charset.StandardCharsets.UTF_8);
        }

        // 否则直接当 key 返回
        return input;
    }

    /**
     * 临时密钥 - 使用Redis缓存避免频繁调用腾讯云API
     * @param cosKey 用户区域
     * @return 临时密钥只有30min，缓存25min
     */
    public Response getTmpSecret(String cosKey) {
        // 构建Redis缓存key
        String cacheKey = REDIS_COS_SECRET_PREFIX + cosKey ;
        try {
            // 1. 先从Redis获取缓存的临时密钥
            Response cachedResponse = (Response) redisTemplate.opsForValue().get(cacheKey);
            if (cachedResponse != null) {
                log.info("从Redis缓存获取到临时密钥，cosKey: {}", cosKey);
                return cachedResponse;
            }
            log.info("Redis缓存中没有临时密钥，调用腾讯云API获取，cosKey: {}", cosKey);
            // 2. 缓存中没有，调用腾讯云API获取新的临时密钥
            TreeMap<String, Object> config = new TreeMap<String, Object>();
            config.put("secretId", secretId);
            config.put("secretKey", secretKey);
            Policy policy = new Policy();
            config.put("durationSeconds", 1800);
            config.put("bucket", bucketName);
            config.put("region", regionName);

            Statement statement = new Statement();
            statement.setEffect("allow");
            statement.addActions(new String[]{
                    "cos:PutObject",
                    "cos:DeleteObject",
                    "cos:HeadObject",
                    "cos:PostObject",
                    "cos:InitiateMultipartUpload",
                    "cos:ListMultipartUploads",
                    "cos:ListParts",
                    "cos:GetObject",
                    "cos:UploadPart",
                    "cos:CompleteMultipartUpload",
                    "ci:CreateMediaJobs",
                    "ci:CreateFileProcessJobs"
            });
            statement.addResources(new String[]{
                    "qcs::cos:ap-chengdu:uid/1330222639:lichuyi-1330222639/" + cosKey + "/*",
                    "qcs::ci:ap-chengdu:uid/1330222639:lichuyi-1330222639/"+cosKey+ "/*"
            });
            policy.addStatement(statement);
            config.put("policy", Jackson.toJsonPrettyString(policy));

            Response response = CosStsClient.getCredential(config);

            // 3. 将新获取的临时密钥存入Redis缓存，过期时间25分钟
            redisTemplate.opsForValue().set(cacheKey, response, CACHE_EXPIRE_MINUTES, TimeUnit.MINUTES);
            log.info("临时密钥已缓存到Redis，cosKey: {}, 缓存时间: {}分钟", cosKey, CACHE_EXPIRE_MINUTES);

            return response;
        } catch (Exception e) {
            log.error("获取临时密钥失败，cosKey: {}", cosKey, e);
            throw new IllegalArgumentException("no valid secret !");
        }
    }
    /**
     * 使用临时密钥，获取签名 返回的是完整的Authorization头信息供前端使用
     * @param cosKey 用户区域
     * @param fileName 文件名或URL
     * @return 30min有效期的Authorization头信息
     */
    public Map<String, String> getSignatureForGetAndHead(String cosKey, String fileName) {
        Response resp = getTmpSecret(cosKey);
        COSCredentials cred = new BasicSessionCredentials(
                resp.credentials.tmpSecretId,
                resp.credentials.tmpSecretKey,
                resp.credentials.sessionToken
        );
        COSSigner signer = new COSSigner();
        Map<String, String> headers = new HashMap<>();
        String hostUrl = bucketName + ".cos." + regionName + ".myqcloud.com";
        headers.put(Headers.HOST, hostUrl);
        Map<String, String> params = new HashMap<>();
        Date start = new Date();
        Date end = new Date(System.currentTimeMillis() + 30L*60L*1000L);
        String key = getKey(fileName);

        Map<String, String> result = new HashMap<>();
        result.put("getAuthorization", signer.buildAuthorizationStr(HttpMethodName.GET, key, headers, params, cred, start, end));
        result.put("headAuthorization", signer.buildAuthorizationStr(HttpMethodName.HEAD, key, headers, params, cred, start, end));
        result.put("x-cos-security-token", resp.credentials.sessionToken);
        result.put("Host", hostUrl);
        return result;
    }
    /**
     * 使用临时密钥，获取签名 - get
     * @param fileName //文件url或者 key
     * @param cosKey //用户区
     * @return 30min有效期的签名
     */
    public String getSignature(String cosKey,String fileName ) throws Exception {
        // 1. 获取临时密钥
        Response tmpSecret = this.getTmpSecret(cosKey);
        COSCredentials cred = new BasicSessionCredentials(
                tmpSecret.credentials.tmpSecretId,
                tmpSecret.credentials.tmpSecretKey,
                tmpSecret.credentials.sessionToken
        );
        // 2. 获取对象 key（去掉 URL 前缀）
        String key = getKey(fileName);
        // 3. 创建 COSClient
        ClientConfig clientConfig = new ClientConfig(new Region(regionName));
        COSClient cosClient = new COSClient(cred, clientConfig);
        Map<String, String> headers = new HashMap<String, String>();
        Map<String, String> params = new HashMap<String, String>();
        // 4. 设置过期时间（30 分钟）
        Date expirationDate = new Date(System.currentTimeMillis() + 30 * 60 * 1000L);
        URL url = cosClient.generatePresignedUrl(bucketName, key, expirationDate, HttpMethodName.GET, headers, params);
        // 关闭 COSClient
        cosClient.shutdown();
        // 5. 返回签名 URL
        log.info("返回的get - 签名信息：{}",url.toString());
        return url.toString();
    }

    /**
     * head 签名
     * @param cosKey
     * @param fileName
     * @return
     */
    public String headSignature(String cosKey,String fileName ) throws Exception {
        // 1. 获取临时密钥
        Response tmpSecret = this.getTmpSecret(cosKey);
        COSCredentials cred = new BasicSessionCredentials(
                tmpSecret.credentials.tmpSecretId,
                tmpSecret.credentials.tmpSecretKey,
                tmpSecret.credentials.sessionToken
        );
        // 2. 获取对象 key（去掉 URL 前缀）
        String key = getKey(fileName);
        // 3. 创建 COSClient
        ClientConfig clientConfig = new ClientConfig(new Region(regionName));
        COSClient cosClient = new COSClient(cred, clientConfig);
        Map<String, String> headers = new HashMap<String, String>();
        Map<String, String> params = new HashMap<String, String>();
        // 4. 设置过期时间（30 分钟）
        log.info("开始生成key{}",key);
        Date expirationDate = new Date(System.currentTimeMillis() + 30 * 60 * 1000L);
        URL url = cosClient.generatePresignedUrl(bucketName, key, expirationDate, HttpMethodName.HEAD, headers, params);
        // 关闭 COSClient
        cosClient.shutdown();
        // 5. 返回签名 URL
        log.info("返回的head - 签名信息：{}",url.toString());
        return url.toString();
    }
    /**
     * 删除对象
     * @param
     * @return 是否删除成功
     */
    public void  deleteObject(String cosKey,String fileName ) {
        // 1. 获取临时密钥
        Response tmpSecret = this.getTmpSecret(cosKey);
        COSCredentials cred = new BasicSessionCredentials(
                tmpSecret.credentials.tmpSecretId,
                tmpSecret.credentials.tmpSecretKey,
                tmpSecret.credentials.sessionToken
        );
        // 2. 获取对象 key（去掉 URL 前缀）
        String key = getKey(fileName);
        // 3. 创建 COSClient
        ClientConfig clientConfig = new ClientConfig(new Region(regionName));
        COSClient cosClient = new COSClient(cred, clientConfig);
        Map<String, String> headers = new HashMap<String, String>();
        headers.put(Headers.HOST, clientConfig.getEndpointBuilder().buildGeneralApiEndpoint(regionName));
        try {
            cosClient.deleteObject(bucketName, key);
        }catch (CosServiceException  e){
            throw new CommonException("COS权限异常!"+e.getErrorMessage());
        }
        // 关闭 COSClient
        cosClient.shutdown();
        // 5. 返回签名 URL
    }

    /**
     * 上传对象
     * @param filePath //本地文件完整路径
     * @param objectName //上传到COS的对象名
     * @param cosKey // 用户区权限
     * @return 文件访问URL
     */
    public String uploadFile(String filePath, String objectName, String cosKey) {
        // 1. 获取临时密钥
        Response tmpSecret = this.getTmpSecret(cosKey);
        COSCredentials cred = new BasicSessionCredentials(
                tmpSecret.credentials.tmpSecretId,
                tmpSecret.credentials.tmpSecretKey,
                tmpSecret.credentials.sessionToken
        );
        // 2. 创建 COSClient
        ClientConfig clientConfig = new ClientConfig(new Region(regionName));
        COSClient cosClient = new COSClient(cred, clientConfig);
        // 构造用户区+文件名
        String key = cosKey + "/" + objectName;
        try {
            File localFile = new File(filePath);
            if (!localFile.exists()) {
                throw new IllegalArgumentException("本地文件不存在: " + filePath);
            }
            // 3. 上传文件
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, localFile);
            PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest);
            System.out.println("上传成功, ETag = " + putObjectResult.getETag());

        } catch (CosServiceException e) {
            throw new CommonException("COS 服务端异常: " + e.getErrorMessage());
        } catch (CosClientException e) {
            throw new CommonException("COS 客户端异常: " + e.getMessage());
        } finally {
            if (cosClient != null) {
                cosClient.shutdown();
            }
        }
        // 4. 构建并返回未编码的 URL
        StringBuilder urlBuilder = new StringBuilder("https://");
        urlBuilder.append(bucketName)
                .append(".cos.")
                .append(regionName)
                .append(".myqcloud.com/")
                .append(key);
        log.info("上传成功，文件访问URL: {}", urlBuilder.toString());
        return urlBuilder.toString();
    }

}
